Skip to content

Document the streaming attachment transport (JS SDK) - #563

Open
khawarizmus wants to merge 3 commits into
mainfrom
attachment-transport
Open

Document the streaming attachment transport (JS SDK)#563
khawarizmus wants to merge 3 commits into
mainfrom
attachment-transport

Conversation

@khawarizmus

@khawarizmus khawarizmus commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Documents the streaming attachment transport shipped in powersync-js powersync-ja/powersync-js#1039 (@powersync/common@2.1.0), and fixes a few pre-existing issues in the JS examples found along the way.

New content (all JavaScript/TypeScript only; other SDK tabs untouched):

  • "Attachment Transport" section under Core Components: the default buffered path, the AttachmentTransportAdapter interface, and the either-remoteStorage-or-transportAdapter configuration rule
  • "Transferring Large Files Without Buffering" under Advanced Topics: the three streaming transports (Expo, React Native FS, Node.js) created via createTransportAdapter, with a full Expo example
  • "Custom Transport Adapters": why and how to build your own (resumable transfers, encryption), with a skeleton
  • saveFileFromUri example for registering on-disk files without buffering, plus StreamingLocalStorageAdapter coverage in the Local Storage Adapter section
  • Version requirements: web v3.0.0, React Native v2.0.3, Node v0.21.0, attachments-storage-react-native v0.1.0

Fixes:

  • JS examples compared state === 'SYNCED', but AttachmentState is a numeric enum, so the checks never matched
  • The ProfilePhoto web example passed local_uri (an indexeddb:// reference) straight to <img src>, which fails; it now converts through the storage adapter to an object URL, with a note explaining the platform difference

The content in this PR was AI-assisted using Claude Code.

…t SDK

- Clarified the format of `localUri` for local storage references.
- Added details about the `Attachment Transport` and its role in managing remote operations.
- Introduced the concept of a streaming transport for large files, including configuration examples.
- Updated notes on the React Native local storage adapter requirements.
- Provided additional context on the `Attachment Queue` and its lifecycle management.

This update aims to improve clarity and usability for developers working with attachments.
@mintlify

mintlify Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
powersync 🟢 Ready View Preview Aug 13, 2026, 4:58 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

Comment thread client-sdks/advanced/attachments.mdx Outdated
Comment thread client-sdks/advanced/attachments.mdx
Comment thread client-sdks/advanced/attachments.mdx Outdated
Comment thread client-sdks/advanced/attachments.mdx Outdated
Comment thread client-sdks/advanced/attachments.mdx Outdated
Comment thread client-sdks/advanced/attachments.mdx Outdated
Comment thread client-sdks/advanced/attachments.mdx
Comment thread client-sdks/advanced/attachments.mdx Outdated
Comment thread client-sdks/advanced/attachments.mdx Outdated
Comment thread client-sdks/advanced/attachments.mdx
Comment thread client-sdks/advanced/attachments.mdx Outdated
- Updated the description of the remote storage adapter to emphasize the use of streaming transports for large file transfers.
- Removed outdated sections on the Attachment Transport, consolidating information for clarity.
- Enhanced the explanation of the Attachment Queue and its role in managing attachment lifecycles.
- Clarified the handling of `localUri` in the web SDK and its implications for image loading.

These changes aim to improve the documentation's clarity and usability for developers working with attachments.
- `downloadFile(attachment)` - Download file from cloud storage
- `deleteFile(attachment)` - Delete file from cloud storage

In the JavaScript/TypeScript SDK, apps that transfer large files can replace this adapter with a streaming transport; see [Transferring Large Files Without Buffering](#transferring-large-files-without-buffering).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we calling it TypeScript SDK anywhere else?

Suggested change
In the JavaScript/TypeScript SDK, apps that transfer large files can replace this adapter with a streaming transport; see [Transferring Large Files Without Buffering](#transferring-large-files-without-buffering).
In the JavaScript SDK, apps that transfer large files can replace this adapter with a streaming transport; see [Transferring Large Files Without Buffering](#transferring-large-files-without-buffering).

This also still makes it sound like all other SDKs necessarily buffer the file, when that is not true for Kotlin and Dart.

- **Performs cleanup** - Removes archived files that are no longer needed
- **Verifies integrity** - Checks local files exist and repairs inconsistencies

In the JavaScript/TypeScript SDK, remote transfers buffer each file through JS memory by default. Apps that handle large files can swap in a streaming transport adapter; see [Transferring Large Files Without Buffering](#transferring-large-files-without-buffering).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's also only mention this once (it's also mentioned under attachment table, I don't think it belongs there).

- **Resumable transfers** - The queue retries a failed operation by calling the transport again on the next sync interval. A transport built on a resumable protocol such as [tus](https://tus.io) or S3 multipart upload can continue from the last confirmed offset instead of restarting from zero. Downloads can resume a partial file with HTTP `Range` requests
- **Encryption** - Encrypt files before upload and decrypt them after download for end-to-end encrypted attachments, without holding the whole file in memory

```typescript

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of this empty snippet, linking to the relevant interface in our tsdoc reference might be better.

Comment on lines +2502 to +2503

Throwing from any method marks the operation as failed; the queue retries it on the next sync interval, subject to your [error handler](#error-handling).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO this is obvious enough that it doesn't need to be mentioned.

Suggested change
Throwing from any method marks the operation as failed; the queue retries it on the next sync interval, subject to your [error handler](#error-handling).

// saveFileFromUri queues the upload without reading the file into memory.
// Requires a streaming-capable local storage adapter (StreamingLocalStorageAdapter:
// Node.js, Expo, or React Native FS; not available on web).
async function attachRecording(localUri: string, recordingId: string) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should add a second snippet for this (to have one with a blob and one streaming example) instead of combining it into one?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants